
/*========================================================================================
 *                                                                                      *
 *               Copyright(c) 2013-2014, Cha SangWoo, All rights reserved.              *
 *                                                                                      *
 * This file is part of ChaOS.                                                          *
 *                                                                                      *
 * Permission to use, copy, modify, and distribute this software in source and binary   *
 * forms and its documentation for any purpose and without fee is hereby granted,       *
 * provided that the above copyright notice appear in all copies and that both that     *
 * copyright notice and this permission notice appear in supporting documentation.      *
 *                                                                                      *
 * THIS SOFTWARE IS PROVIDED BY THE CHA SANGWOO AND CONTRIBUTORS "AS IS" AND ANY        *
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED            *
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.   *
 * IN NO EVENT SHALL CHA SANGWOO OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,    *
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT         *
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,          *
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF            *
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE      *
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,                    *
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                                   *
 *                                                                                      *
========================================================================================*/

#include "co_core.h"
#include "co_semaphore.h"

#ifdef __cplusplus
    namespace ChaOS {
#endif

//////////////////////////////////////  < BEGIN >  ///////////////////////////////////////

/*======================================================================================*/
/*!
    @fn             OBJECT co_semaphore_create(UINT16 init_count, UINT16 max_count)

    @brief          Create a semaphore.

    @param [in]     init_count - initial count of semaphore
    @param [in]     max_count  - maximum count of semaphore

    @return         Object for semaphore

    - HISTORY -
    [V00.00.01] 2014-03-23 [aireat] - Created.
*/
/*======================================================================================*/
OBJECT co_semaphore_create(UINT16 init_count, UINT16 max_count)
{
    SEMAPHORE_CB   *psema;

    CO_ASSERT((init_count <= max_count));

    // allocate heap memory
    psema = (SEMAPHORE_CB*) co_heap_alloc(sizeof(SEMAPHORE_CB));
    CO_ASSERT(psema != NULL);

    // set object ID
    OBJID_SET(psema, OBJ_SEMAPHORE_ID);

    // initialize
    psema->cur_count = init_count;
    psema->max_count =  max_count;

    return (OBJECT) psema;
}


/*======================================================================================*/
/*!
    @fn             CO_RESULT co_semaphore_take(OBJECT object, UINT16 timeout)

    @brief          Take a semaphore.

    @param [in]     object  -
    @param [in]     timeout -

    @return         CO_RESULT value

    - HISTORY -
    [V00.00.01] 2014-03-23 [aireat] - Created.
*/
/*======================================================================================*/
CO_RESULT co_semaphore_take(OBJECT object, UINT16 timeout)
{
    SEMAPHORE_CB   *psema = (SEMAPHORE_CB*) object;

    // check object ID
    OBJID_CHECK(psema, OBJ_SEMAPHORE_ID);

    ///////////////////
    CO_ENTER_CRITICAL();

    if (psema->cur_count > 0)
    {
        psema->cur_count--;

        CO_LEAVE_CRITICAL();
        ////////////////////

        return (CO_RESULT) CO_OK_SUCCESS;
    }
    // Do not preocess in ISR
    else if ((_g_nested_count == CO_NESTED_COUNT_USER) && 
             (timeout))
    {
        THREAD_CB   *pthread = GET_CURRENT_THREAD();

        pthread->state  = CO_THREAD_WAIT_OBJECT;
        pthread->wait_object = object;

        if (timeout != WAIT_FOREVER)
        {
            pthread->state |= CO_THREAD_WAIT_TIME;
            pthread->wait_time = PORT_GET_TICK_IN_MS(timeout);
        }

        _core_add_block_list(pthread);

        CO_LEAVE_CRITICAL();
        ////////////////////

        PORT_SWITCH_IN_THREAD();

        return (CO_RESULT) pthread->result;
    }
    else
    {
        CO_LEAVE_CRITICAL();
        ////////////////////

        return (CO_RESULT) CO_NG_USED;
    }
}


/*======================================================================================*/
/*!
    @fn             VOID co_semaphore_give(OBJECT object)

    @brief          Give a semaphore.

    @param [in]     - object for semaphre

    @return         None

    - HISTORY -
    [V00.00.01] 2014-03-23 [aireat] - Created.
*/
/*======================================================================================*/
VOID co_semaphore_give(OBJECT object)
{
    SEMAPHORE_CB   *psema = (SEMAPHORE_CB*) object;

    // check object ID
    OBJID_CHECK(psema, OBJ_SEMAPHORE_ID);

    ///////////////////
    CO_ENTER_CRITICAL();

    if (_core_search_thread_in_block_list(object) == NULL)
    {
        psema->cur_count++;
        psema->cur_count = CO_MIN(psema->cur_count, psema->max_count);
    }

    CO_LEAVE_CRITICAL();
    ////////////////////
}


/*======================================================================================*/
/*!
    @fn             UINT16 co_semaphore_get_max_count(OBJECT object)

    @brief          Get maximum count of semaphore

    @param [in]     object - object for semaphre

    @return         Maximum count

    - HISTORY -
    [V00.00.01] 2014-03-23 [aireat] - Created.
*/
/*======================================================================================*/
UINT16 co_semaphore_get_max_count(OBJECT object)
{
    SEMAPHORE_CB   *psema = (SEMAPHORE_CB*) object;

    // check object ID
    OBJID_CHECK(psema, OBJ_SEMAPHORE_ID);

    return (psema->max_count);
}


/*======================================================================================*/
/*!
    @fn             UINT16 co_semaphore_get_cur_count(OBJECT object)

    @brief          Get maximum count of semaphore

    @param [in]     object - object for semaphre

    @return         Maximum count

    - HISTORY -
    [V00.00.01] 2014-03-23 [aireat] - Created.
*/
/*======================================================================================*/
UINT16 co_semaphore_get_cur_count(OBJECT object)
{
    SEMAPHORE_CB   *psema = (SEMAPHORE_CB*) object;

    // check object ID
    OBJID_CHECK(psema, OBJ_SEMAPHORE_ID);

    return (psema->cur_count);
}


/*======================================================================================*/
/*!
    @fn             UINT16 co_semaphore_get_remain_count(OBJECT object)

    @brief          Get maximum count of semaphore

    @param [in]     object - object for semaphre

    @return         

    - HISTORY -
    [V00.00.01] 2014-03-23 [aireat] - Created.
*/
/*======================================================================================*/
UINT16 co_semaphore_get_remain_count(OBJECT object)
{
    SEMAPHORE_CB   *psema = (SEMAPHORE_CB*) object;

    // check object ID
    OBJID_CHECK(psema, OBJ_SEMAPHORE_ID);

    return (psema->max_count - psema->cur_count);
}


/*======================================================================================*/
/*!
    @fn             VOID co_semaphore_reset_count(OBJECT object)

    @brief          

    @param [in]     object - object for semaphre

    @return         None

    - HISTORY -
    [V00.00.01] 2014-03-23 [aireat] - Created.
*/
/*======================================================================================*/
VOID co_semaphore_reset_count(OBJECT object)
{
    SEMAPHORE_CB    *psema = (SEMAPHORE_CB*) object;
    THREAD_CB       *pthread;

    // check object ID
    OBJID_CHECK(psema, OBJ_SEMAPHORE_ID);

    ///////////////////
    CO_ENTER_CRITICAL();

    psema->cur_count = 0;

    // release thread waiting object
    while ((pthread = _core_search_thread_in_block_list(object)))
        pthread->result = CO_NG_RESET;

    CO_LEAVE_CRITICAL();
    ////////////////////
}


/*======================================================================================*/
/*!
    @fn             VOID co_semaphore_add_count(OBJECT object, UINT16 count);

    @brief          

    @param [in]     object - object for semaphre

    @return         None

    - HISTORY -
    [V00.00.01] 2014-03-23 [aireat] - Created.
*/
/*======================================================================================*/
VOID co_semaphore_add_count(OBJECT object, UINT16 count)
{
    SEMAPHORE_CB    *psema = (SEMAPHORE_CB*) object;

    // check object ID
    OBJID_CHECK(psema, OBJ_SEMAPHORE_ID);

    ///////////////////
    CO_ENTER_CRITICAL();

    while (count > 0)
    {
        if (_core_search_thread_in_block_list(object) == NULL)
        {
            psema->cur_count += count;
            psema->cur_count = CO_MIN(psema->cur_count, psema->max_count);
            break;
        }
        
        count--;
    }

    CO_LEAVE_CRITICAL();
    ////////////////////
}

/*======================================================================================*/
/*!
    @fn             VOID co_semaphore_sub_count(OBJECT object, UINT16 count);

    @brief          

    @param [in]     object - object for semaphre

    @return         None

    - HISTORY -
    [V00.00.01] 2014-03-23 [aireat] - Created.
*/
/*======================================================================================*/
VOID co_semaphore_sub_count(OBJECT object, UINT16 count)
{
    SEMAPHORE_CB    *psema = (SEMAPHORE_CB*) object;

    // check object ID
    OBJID_CHECK(psema, OBJ_SEMAPHORE_ID);

    ///////////////////
    CO_ENTER_CRITICAL();

    if (count > 0)
    {
        psema->cur_count -= CO_MIN(psema->cur_count, count);
    }

    CO_LEAVE_CRITICAL();
    ////////////////////
}

//////////////////////////////////////  <  END  >  ///////////////////////////////////////

#ifdef __cplusplus
    } /* namespace ChaOS */
#endif

